// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System;
using NUnit.Framework;
using ValidationAspects;
using ValidationAspects.Core;
using ValidationAspects.Sdk;

namespace TestValidationAspects.Core
{
	[TestFixture]
	public class TestValidatorCache
	{
		#region GetValidator
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_GetValidator_throws_for_null_param()
		{
			new ValidatorCache<object>().GetValidator(null);
		}

		[Test]
		public void Test_GetValidator_returns_null_for_key_not_found()
		{
			Assert.IsNull(new ValidatorCache<object>().GetValidator(new object()));
		}
		#endregion

		#region SetValidator
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_SetValidator_throws_for_null_key_param()
		{
			new ValidatorCache<object>().SetValidator(null, new MockValidator());
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_SetValidator_throws_for_null_validator_param()
		{
			new ValidatorCache<object>().SetValidator(new object(), null);
		}

		[Test]
		public void Test_SetValidator_GetValidator()
		{
			object key = new object();
			var cache = new ValidatorCache<object>();
			MockValidator validator = new MockValidator();

			cache.SetValidator(key, validator);
			Assert.AreSame(validator, cache.GetValidator(key));
		}

		[Test]
		public void Test_SetValidator_overwrites()
		{
			object key = new object();
			var cache = new ValidatorCache<object>();
			MockValidator validator1 = new MockValidator();
			MockValidator validator2 = new MockValidator();

			cache.SetValidator(key, validator1);
			Assert.AreSame(validator1, cache.GetValidator(key));

			cache.SetValidator(key, validator2);
			Assert.AreSame(validator2, cache.GetValidator(key));
		}
		#endregion

		#region GetFactories
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_GetFactories_throws_for_null_param()
		{
			new ValidatorCache<object>().GetFactories(null);
		}

		[Test]
		public void Test_GetFactories_returns_null_for_key_not_found()
		{
			Assert.IsNull(new ValidatorCache<object>().GetFactories(new object()));
		}
		#endregion

		#region SetFactories
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_SetFactories_throws_for_null_key_param()
		{
			new ValidatorCache<object>().SetFactories(null, new[] {new MockFactory()});
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_SetFactories_throws_for_null_validatorfactory_param()
		{
			new ValidatorCache<object>().SetFactories(new object(), null);
		}

		[Test]
		public void Test_SetFactories_GetFactories()
		{
			object key = new object();
			var cache = new ValidatorCache<object>();
			IValidatorFactory[] factories = new[] {new MockFactory()};

			cache.SetFactories(key, factories);
			Assert.AreSame(factories, cache.GetFactories(key));
		}

		[Test]
		public void Test_SetFactories_overwrites()
		{
			object key = new object();
			var cache = new ValidatorCache<object>();
			IValidatorFactory[] factories1 = new[] {new MockFactory()};
			IValidatorFactory[] factories2 = new[] {new MockFactory()};

			cache.SetFactories(key, factories1);
			Assert.AreSame(factories1, cache.GetFactories(key));

			cache.SetFactories(key, factories2);
			Assert.AreSame(factories2, cache.GetFactories(key));
		}

		[Test]
		public void Test_SetFactories_clears_cached_validator_for_key()
		{
			object key = new object();
			var cache = new ValidatorCache<object>();

			cache.SetValidator(key, new MockValidator());
			Assert.IsNotNull(cache.GetValidator(key));

			cache.SetFactories(key, new[] {new MockFactory()});

			Assert.IsNull(cache.GetValidator(key));
		}
		#endregion

		#region Test Clear
		[Test]
		public void Test_Clear()
		{
			object keyA = new object();
			object keyB = new object();

			var cache = new ValidatorCache<object>();

			cache.SetValidator(keyA, new MockValidator());
			cache.SetValidator(keyB, new MockValidator());
			Assert.IsNotNull(cache.GetValidator(keyA));
			Assert.IsNotNull(cache.GetValidator(keyB));

			cache.SetFactories(keyA, new[] {new MockFactory()});
			cache.SetFactories(keyB, new[] {new MockFactory()});
			Assert.IsNotNull(cache.GetFactories(keyA));
			Assert.IsNotNull(cache.GetFactories(keyB));

			cache.Clear();

			Assert.IsNull(cache.GetValidator(keyA));
			Assert.IsNull(cache.GetValidator(keyB));

			Assert.IsNull(cache.GetFactories(keyA));
			Assert.IsNull(cache.GetFactories(keyB));
		}
		#endregion

		#region Test Clear(T key)
		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void Test_Clear_key_throws_for_null_key_param()
		{
			new ValidatorCache<object>().Clear(null);
		}

		[Test]
		public void Test_Clear_key()
		{
			object key = new object();
			var cache = new ValidatorCache<object>();

			cache.SetValidator(key, new MockValidator());
			Assert.IsNotNull(cache.GetValidator(key));

			cache.SetFactories(key, new[] {new MockFactory()});
			Assert.IsNotNull(cache.GetFactories(key));

			cache.Clear(key);
			Assert.IsNull(cache.GetValidator(key));
			Assert.IsNull(cache.GetFactories(key));
		}
		#endregion

		#region Mocks
		public class MockValidator : IValidator
		{
			#region Implementation of IValidator
			public void Invoke(object value, IValidationContext context)
			{
				throw new NotImplementedException();
			}
			#endregion
		}

		public class MockFactory : IValidatorFactory
		{
			#region Implementation of IValidatorFactory
			public IValidator<T> Create<T>()
			{
				throw new NotImplementedException();
			}
			#endregion
		}
		#endregion
	}
}